﻿﻿/*#
# Copyright 2012 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This code is not supported by Google
#
*/
/*
 * Submit Google Search Appliance search requests and parses the response xml search results.
 * 
 * To use, see the 'main' method of GSAClient.java:
 *    * define the collection, frontend, GSA hostname
 *    * submit a search query
 *    * define filters (restricts)
 *    * define the startPage
 *    * parse the response stream for the results, item attributes for each result,
 *      spelling suggestions and facet counts
 *    
 * http://www.google.com/support/enterprise/static/gsa/docs/admin/70/gsa_doc_set/xml_reference/
 * 
 * 
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Web;
using System.IO;
using System.Xml;
using System.Diagnostics;


namespace GSAClient
{
    class GSAClient
    {
        private String host;
        private String query;
        private String collection;
        private String front_end;
        private int start = 0;
        private int maxResults = 10;
        private List<String> restricts = new List<String>();
        private String sSource = "GSAClient";
        private String sLog = "Application";

        static void Main(string[] args)
        {
            String host = "http://YOUR_GSA";
            String query = "san francisco";
            String collection = "your_collection";
            String front_end = "your_frontend";

            GSAClient g = new GSAClient(host, collection, front_end);
            g.setQuery(query);
            g.setMaxResults(2);
            g.addRestrict("price:20..30");
            g.addRestrict("brand=acme");
            g.setStartPage(0);
            Result r = g.execute();

            if (r != null)
            {
                Console.WriteLine("Elapsed Time: " + Convert.ToString(r.getElapsedTime()));
                Console.WriteLine("TotalResults " + Convert.ToString(r.getTotalResults()));
                foreach (KeyValuePair<String, String> entry in r.getQueryParams())
                    Console.WriteLine("Query Parameter: " + entry.Key + " " + entry.Value);

                foreach (String s in r.getRestricts())
                    Console.WriteLine("Restricts: " + s);

                if (r.has_spelling_suggestions())
                    foreach (String s in r.getSpellingSuggestions())
                        Console.WriteLine("Spelling suggestion: " + s);

                if (r.has_keymatches())
                    foreach (Dictionary<String, String> d in r.getKeyMatches())
                    {
                        Console.WriteLine("Keymatche Title: " + d[Result.KEYMATCH_TITLE]);
                        Console.WriteLine("Keymatche URL: " + d[Result.KEYMATCH_URL]);
                    }

                if (r.has_oneboxresults())
                {
                    foreach (OneBoxResult obr in r.getOneBoxResults())
                    {
                        if (obr.isPromotion())
                            Console.WriteLine("Promotion ");

                        if (obr.isRedirect())
                            Console.WriteLine("Redirect");

                        foreach (KeyValuePair<String, String> entry in obr.getKVPairs())
                            Console.WriteLine("Onebox Parameter: " + entry.Key + " " + entry.Value);

                    }
                }

                foreach (Product p in r.getProducts())
                {
                    Console.WriteLine(p.getTitle());
                    foreach (String s in p.getAttribute("category_name"))
                        Console.WriteLine("Category name: " + s);
                }

                foreach (Facet f in r.getFacets())
                {
                    Console.WriteLine(f.getName() + " Display Name: " + f.getDisplayName());
                    foreach (FacetValue fv in f.getFacetValues())
                        if (!f.isRangeValue())
                            Console.WriteLine(fv.getName() + " --> " + fv.getCount());
                        else
                            Console.WriteLine(fv.getMin() + "..." + fv.getMax() + " --> " + fv.getCount());
                }

            }

            Console.WriteLine("Done");
            Console.ReadLine();
        }

        public GSAClient(String host, String collection, String front_end)
        {
            this.host = host;
            this.collection = collection;
            this.front_end = front_end;


            if (!EventLog.SourceExists(sSource))
                EventLog.CreateEventSource(sSource, sLog);
            EventLog.WriteEntry(sSource, "Initialize GSAClient", EventLogEntryType.Information);
        }


        public void setQuery(String query)
        {
            this.query = HttpUtility.UrlEncode(query);
        }

        public void addRestrict(String restrict)
        {
            this.restricts.Add(restrict);
        }

        public void setStartPage(int start)
        {
            this.start = start;
        }

        public List<String> getRestricts()
        {
            return this.restricts;
        }

        public void setMaxResults(int maxResults)
        {
            this.maxResults = maxResults;
        }

        public Result execute()
        {
            String dnav = "";
            foreach (String s in this.restricts)
                dnav = dnav + HttpUtility.UrlEncode(" inmeta:" + s);
            String query_string = host + "/search?q=" + query + "&client=" + front_end + "&output=xml_no_dtd&getfields=*&filter=0&site=" + collection + "&start=" + Convert.ToString(this.start) + "&num=" + Convert.ToString(this.maxResults) + "&as_q=" + dnav;
            EventLog.WriteEntry(sSource, "Query Request: " + query_string, EventLogEntryType.Information);
            String responseFromServer = "";
            try
            {
                ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(AcceptAllCertifications);
                HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(query_string);
                HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
                if (resp.StatusCode == HttpStatusCode.OK)
                {
                    StreamReader sr = new StreamReader(resp.GetResponseStream());
                    responseFromServer = sr.ReadToEnd().Trim();
                    //EventLog.WriteEntry(sSource, "Query Response " + responseFromServer, EventLogEntryType.Information);
                    return parseResponse(responseFromServer);
                }
                return null;
            }
            catch (WebException ex)
            {
                EventLog.WriteEntry(sSource, "Unable to execute query " + ex.Message, EventLogEntryType.Error);
                return null;
            }

        }

        private Result parseResponse(String responseFromServer)
        {
            try
            {
                XmlDocument xdoc = new XmlDocument();
                try
                {
                    xdoc.LoadXml(responseFromServer);
                }
                catch (Exception ex)
                {
                    EventLog.WriteEntry(sSource, "Unable to parse XML " + ex, EventLogEntryType.Error);
                }

                Result r = new Result();

                XmlNamespaceManager nsMgr = new XmlNamespaceManager(xdoc.NameTable);
                nsMgr.AddNamespace("atom", "http://www.w3.org/2005/Atom");

                XmlNode n_elapsed_time = xdoc.SelectSingleNode("/GSP/TM", nsMgr);
                XmlNode n_q = xdoc.SelectSingleNode("/GSP/Q", nsMgr);
                XmlNode n_totalResults = xdoc.SelectSingleNode("/GSP/RES/M", nsMgr);
                XmlNodeList nl_params = xdoc.SelectNodes("/GSP/PARAM", nsMgr);
                XmlNodeList nl_oneboxresults = xdoc.SelectNodes("/GSP/ENTOBRESULTS/OBRES/MODULE_RESULT", nsMgr);
                XmlNodeList nl_spelling_suggestion = xdoc.SelectNodes("/GSP/Spelling/Suggestion", nsMgr);
                XmlNodeList nl_results = xdoc.SelectNodes("/GSP/RES/R", nsMgr);
                XmlNodeList nl_facets = xdoc.SelectNodes("/GSP/RES/PARM/PMT", nsMgr);
                XmlNodeList nl_keymatches = xdoc.SelectNodes("/GSP/GM", nsMgr);

                r.setElapsedTime(Convert.ToDecimal(n_elapsed_time.InnerText));
                r.setQuery(n_q.InnerText);
                if (n_totalResults != null)
                    r.setTotalResults(Convert.ToInt32(n_totalResults.InnerText));
                else
                    r.setTotalResults(0);

                foreach (XmlNode n_spelling in nl_spelling_suggestion)
                {
                    r.addSpellingSuggestion(n_spelling.Attributes.GetNamedItem("q").Value);
                }

                foreach (XmlNode n_param in nl_params)
                {
                    String key = n_param.Attributes.GetNamedItem("name").Value;
                    String value = n_param.Attributes.GetNamedItem("value").Value;
                    r.addQueryParam(key, value);
                }

                foreach (String s in restricts)
                {
                    r.addRestrict(s);
                }

                foreach (XmlNode n_keymatch in nl_keymatches)
                {
                    XmlNode n_gl = n_keymatch.SelectSingleNode("GL");
                    XmlNode n_gd = n_keymatch.SelectSingleNode("GD");
                    String title = n_gd.FirstChild.Value;
                    String url = n_gl.FirstChild.Value;
                    r.addKeyMatch(title, url);
                }

                foreach (XmlNode n_result in nl_results)
                {
                    Product p = new Product();
                    p.setLink(n_result["U"].InnerText);
                    XmlNodeList nl_attributes = n_result.SelectNodes("MT");
                    foreach (XmlNode n_attribute in nl_attributes)
                    {
                        String key = n_attribute.Attributes.GetNamedItem("N").Value;
                        String value = n_attribute.Attributes.GetNamedItem("V").Value;
                        p.addAttribute(key, value);
                    }
                    r.addProduct(p);
                }

                foreach (XmlNode n_oneboxresult in nl_oneboxresults)
                {
                    OneBoxResult obr = new OneBoxResult();
                    XmlNode n_title = n_oneboxresult.SelectSingleNode("Title");
                    if (n_title != null)
                        obr.setTitle(n_title.FirstChild.Value);
                    XmlNodeList nl_fields = n_oneboxresult.SelectNodes("Field");
                    foreach (XmlNode n_field in nl_fields)
                    {
                        String name = n_field.Attributes.GetNamedItem("name").Value;
                        String value = n_field.FirstChild.Value;
                        obr.addKVPair(name, value);
                    }
                    r.addOneBoxResult(obr);
                }

                foreach (XmlNode n_facet in nl_facets)
                {
                    String name = n_facet.Attributes.GetNamedItem("NM").Value;
                    String display_name = n_facet.Attributes.GetNamedItem("DN").Value;
                    String type = n_facet.Attributes.GetNamedItem("IR").Value;
                    Facet f = new Facet(name,display_name);
                    XmlNodeList nl_facetvalues = n_facet.SelectNodes("PV");
                    foreach (XmlNode n_facetvalue in nl_facetvalues)
                    {
                        if (type == Facet.RANGE)
                        {
                            String min = n_facetvalue.Attributes.GetNamedItem("L").Value;
                            String max = n_facetvalue.Attributes.GetNamedItem("H").Value;
                            String count = n_facetvalue.Attributes.GetNamedItem("C").Value;
                            if (max == "")
                                f.addRangeValue(Convert.ToDouble(min), Double.PositiveInfinity, Convert.ToInt32(count));
                            else
                                f.addRangeValue(Convert.ToDouble(min), Convert.ToDouble(max), Convert.ToInt32(count));
                        }
                        else
                        {
                            String V = n_facetvalue.Attributes.GetNamedItem("V").Value;
                            String count = n_facetvalue.Attributes.GetNamedItem("C").Value;
                            f.addTextValue(V, Convert.ToInt32(count));
                        }
                    }

                    r.addFacet(f);
                }
                return r;

            }
            catch (Exception ex)
            {
                EventLog.WriteEntry(sSource, "Unable to process response  " + responseFromServer + " " + ex.Message, EventLogEntryType.Error);
                return null;
            }
        }

        private bool AcceptAllCertifications(object sender, System.Security.Cryptography.X509Certificates.X509Certificate certification, System.Security.Cryptography.X509Certificates.X509Chain chain, System.Net.Security.SslPolicyErrors sslPolicyErrors)
        {
            return true;
        }


        private String PrettyPrint(XmlDocument doc)
        {
            String resp = "";

            MemoryStream ms = new MemoryStream();
            XmlTextWriter writer = new XmlTextWriter(ms, Encoding.Unicode);
            try
            {
                writer.Formatting = Formatting.Indented;
                doc.WriteContentTo(writer);
                writer.Flush();
                ms.Flush();
                ms.Position = 0;
                StreamReader sr = new StreamReader(ms);
                String FormattedXML = sr.ReadToEnd();
                resp = FormattedXML;
            }
            catch (XmlException ex)
            {
                EventLog.WriteEntry(sSource, "Unable to print xml response" + ex.Message, EventLogEntryType.Error);
            }

            ms.Close();
            writer.Close();
            return resp;
        }
    }
}g